/*******************************************************************************
 * Jimm - Mobile Messaging - J2ME ICQ clone
 * Copyright (C) 2003-05  Jimm Project
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 ********************************************************************************
 * File: src/jimm/FileTransfer.java
 * Version: ###VERSION###  Date: ###DATE###
 * Author(s): Andreas Rossbacher, Dmitry Tunin
 *******************************************************************************/

// #sijapp cond.if modules_FILES="true"#
package jimm;

import DrawControls.text.Par;
import java.io.*;
import javax.microedition.io.*;
import javax.microedition.lcdui.*;
import jimm.chat.ChatTextList;
import jimm.comm.*;
import jimm.modules.*;
import jimm.cl.*;
// #sijapp cond.if modules_FILES="true"#
import jimm.modules.fs.*;
// #sijapp cond.end#
import jimm.modules.photo.*;
import jimm.modules.traffic.*;
import jimm.ui.form.*;
import jimm.util.JLocale;
import protocol.Contact;
import protocol.Protocol;
import protocol.net.TcpSocket;

public final class FileTransfer implements FormListener, FileBrowserListener,
        PhotoListener, Runnable {
    
    // Form for entering the name and description
    private GraphForm name_Desc;
    
    // File data
    private String filename;
    private String description;
    private int sendMode;
    private InputStream fis;
    private int fsize;
    
    // File path and description TextField
    private static final int fileNameField = 1000;
    private static final int descriptionField = 1001;
    private static final int  transferMode = 1002;

    private Par progressInstance;
    private boolean canceled = false;
    
    private Protocol protocol;
    private Contact cItem;
    private ChatTextList chat;

    // #sijapp cond.if modules_ANDROID isnot "true"# //add android fs
    private ViewFinder vf;
    // #sijapp cond.end#                             //add android fs
    private FileSystem file;
    
    // Constructor
    public FileTransfer(Protocol p, Contact _cItem) {
        protocol = p;
        cItem = _cItem;
        chat = p.getChat(_cItem);
    }
        
    // Return the cItem belonging to this FileTransfer
    public Contact getReceiver() {
        return cItem;
    }
    
    // Set the file data
    public void setData(InputStream is, int size) {
        fis = is;
        fsize = size;
    }
    public InputStream getFileIS() {
        return fis;
    }
    public int getFileSize() {
        return fsize;
    }
    
    // Start the file transfer procedure depening on the ft type
    public void startFileTransfer() {
        FileBrowser fsBrowser = new FileBrowser(false);
        fsBrowser.setListener(this);
        fsBrowser.activate();
    }
    // #sijapp cond.if modules_ANDROID isnot "true"# //add android fs
    public static boolean isPhotoSupported() {
        String supports = System.getProperty("video.snapshot.encodings");
        return !StringConvertor.isEmpty(supports);
    }
    public void startPhotoTransfer() {
        // new JimmException(185, 0, true);
        vf = new ViewFinder();
        vf.setPhotoListener(this);
        vf.show();
    }
    // #sijapp cond.end#                             //add android fs
    
    public void onFileSelect(String filename) throws JimmException {
        file = FileSystem.getInstance();
        try {
            file.openFile(filename);
            // FIXME resource leak
            InputStream is = file.openInputStream();
            int size = (int)file.fileSize();
            // Set the file data in file transfer
            setData(is, size);
            // Create filename and ask for name and description
            askForNameDesc(file.getName(), "");
            
        } catch (Exception e) {
            file.close();
            throw new JimmException(191, 3, true);
        }
    }
    
    //
    public void onDirectorySelect(String s0) {}
    
    // Init the ft
    private void initFT() {
        try {
            String name = filename;
            // Windows fix
            name = name.replace(':', '.');
            name = name.replace('/', '_');
            name = name.replace('\\', '_');
            name = name.replace('%', '_');
            protocol.sendFile(this, name, description);
        } catch (Exception e) {
        }
    }
    
    /* Helpers for options UI: */
    private void askForNameDesc(String filename, String description) {
        name_Desc = new GraphForm("name_desc", "ok", "back", this);
        name_Desc.addTextField(fileNameField, "filename", filename, 255, TextField.ANY);
        name_Desc.addTextField(descriptionField, "description", description, 255, TextField.ANY);
        String items = "jimm.net.ru|jimm.org";
        // #sijapp cond.if protocols_JABBER is "true" #
        if (cItem instanceof protocol.jabber.JabberContact) {
            if (cItem.isSingleUserContact() && cItem.isOnline()) {
                items += "|ibb";
            }
        }
        // #sijapp cond.end #
        name_Desc.addSelector(transferMode, "send_via", items, 0);
        name_Desc.addString(JLocale.getString("size") + ": ", String.valueOf(fsize/1024)+" kb");
        // #sijapp cond.if modules_TRAFFIC is "true" #
        int cost = Traffic.calcCost(fsize);
        if (0 < cost) {
            name_Desc.addString(JLocale.getString("cost") + ": ",
                    Traffic.costToString(cost));
        }
        // #sijapp cond.end #
        name_Desc.show();
    }
    
    // Command listener
    public void formAction(GraphForm form, boolean apply) {
        if (apply) {
            filename = name_Desc.getTextFieldValue(fileNameField);
            description = name_Desc.getTextFieldValue(descriptionField);
            sendMode = name_Desc.getSelectorValue(transferMode);
            if (name_Desc.getSelectorValue(transferMode) == 2) {
                initFT();
            } else {
                setProgress(0);
                new Thread(this).start();
            }
        } else {
            destroy();
            ContactList.getInstance().activate();
        }
    }

    public boolean is(Par par) {
        return (progressInstance == par);
    }
    private String getProgressText() {
        return filename + " - " + StringConvertor.bytesToSizeString(fsize, false);
    }
    private void changeFileProgress(String message) {
        if (cItem.hasChat()) {
            chat.changeFileProgress(progressInstance,
                    JLocale.getEllipsisString("sending_file"),
                    getProgressText() + "\n"
                    + JLocale.getString(message));
        }
    }
    
    public void cancel() {
        canceled = true;
        changeFileProgress("canceled");
        if (0 < sendMode) {
            closeFile();
        }
    }
    public boolean isCanceled() {
        return canceled;
    }
    public void setProgress(int percent) {
        try {
            _setProgress(percent);
        } catch (Exception e) {
        }
    }
    public void _setProgress(int percent) {
        if (isCanceled()) {
            return;
        }
        if (-1 == percent) {
            percent = 100;
        }
        if ((0 == percent) && (null == progressInstance)) {
            progressInstance = chat.addFileProgress(JLocale.getEllipsisString("sending_file"), getProgressText());
            chat.activate();
            ContactList.getInstance().addTransfer(this);
            return;
        }
        if (100 == percent) {
            ContactList.getInstance().removeTransfer(progressInstance, false);
            changeFileProgress("complete");
            return;
        }
        progressInstance.setProgress((byte)percent);
        if (cItem.hasChat()) {
            chat.invalidate();
        }
    }
    private void handleException(JimmException e) {
        destroy();
        if (isCanceled()) {
            return;
        }
        if (null != progressInstance) {
            changeFileProgress(JLocale.getString("error") + "\n" + e.getMessage());
        }
    }
    
    private void closeFile() {
        try {
            if (null != fis) {
                fis.close();
            }
        } catch (Exception e) {
        }
        if (null != file) {
            file.close();
        }
        fis = null;
    }
    public void destroy() {
        try {
            closeFile();
            ContactList.getInstance().removeTransfer(progressInstance, false);
            // #sijapp cond.if modules_ANDROID isnot "true"# //add android fs
            if (null != vf) {
                vf.dismiss();
            }
            // #sijapp cond.end#                             //add android fs
            name_Desc.clearForm();
            System.gc();
        } catch (Exception e) {
        }
    }
    
    /** ************************************************************************* */
    public void run() {
        try {
            switch (sendMode) {
                case 0:
                    sendFileThroughServer();
                    break;
                case 1:
                    sendFileThroughWeb();
                    break;
            }
            
        } catch(JimmException e) {
            handleException(e);
            
        } catch(Exception e) {
            // #sijapp cond.if modules_DEBUGLOG is "true" #
            DebugLog.panic("FileTransfer run", e);
            // #sijapp cond.end#
            handleException(new JimmException(194, 2));
        }
        destroy();
    }
    private void sendFileThroughServer() throws JimmException {
        TcpSocket socket = new TcpSocket();
        final int version = 0;
        try {
            socket.connectTo("socket://files.jimm.net.ru:2000");
            
            Util header = new Util();
            header.writeWordBE(version);
            header.writeLenAndUtf8String(filename);
            header.writeLenAndUtf8String(description);
            header.writeDWordBE(fsize);
            socket.write(header.toByteArray());
            socket.flush();
            
            byte[] buffer = new byte[4*1024];
            int counter = fsize;
            while (counter > 0) {
                int read = fis.read(buffer);
                socket.write(buffer, 0, read);
                counter -= read;
                if (fsize != 0) {
                    if (isCanceled()) {
                        throw new JimmException(194, 1);
                    }
                    socket.flush();
                    setProgress((100 - 2) * (fsize - counter) / fsize);
                }
            }
            socket.flush();

            int length = socket.read();
            if (-1 == length) {
                throw new JimmException(120, 13);
            }
            socket.read(buffer, 0, length);
            String url = StringConvertor.utf8beByteArrayToString(buffer, 0, length);

            if (isCanceled()) {
                throw new JimmException(194, 1);
            }
            // Send info about file
            StringBuffer messText = new StringBuffer();
            if (!StringConvertor.isEmpty(description)) {
                messText.append(description).append("\n");
            }
            messText.append("File: ").append(filename).append("\n");
            messText.append("Size: ")
                    .append(StringConvertor.bytesToSizeString(fsize, false))
                    .append("\n");
            messText.append("Link: ").append(url);
            
            protocol.sendMessage(cItem, messText.toString(), true);
            setProgress(100);
            socket.close();
            
        } catch (JimmException e) {
            // #sijapp cond.if modules_DEBUGLOG is "true" #
            DebugLog.panic("send file", e);
            // #sijapp cond.end#
            socket.close();
            throw e;

        } catch (Exception e) {
            // #sijapp cond.if modules_DEBUGLOG is "true" #
            DebugLog.panic("send file", e);
            // #sijapp cond.end#
            socket.close();
            throw new JimmException(194, 0);
        }
    }
    private void sendFileThroughWeb() throws JimmException {
        InputStream is = null;
        OutputStream os = null;
        HttpConnection sc = null;
        
        String host = "filetransfer.jimm.org";
        final String url = "http://" + host + "/__receive_file.php";
        try {
            sc = (HttpConnection) Connector.open(url, Connector.READ_WRITE);
            sc.setRequestMethod(HttpConnection.POST);
            
            String boundary = "a9f843c9b8a736e53c40f598d434d283e4d9ff72";
            
            sc.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
            os = sc.openOutputStream();
            
            // Send post header
            StringBuffer buffer2 = new StringBuffer();
            buffer2.append("--").append(boundary).append("\r\n");
            buffer2.append("Content-Disposition: form-data; name=\"filedesc\"\r\n");
            buffer2.append("\r\n");
            buffer2.append(description);
            buffer2.append("\r\n");
            buffer2.append("--").append(boundary).append("\r\n");
            buffer2.append("Content-Disposition: form-data; name=\"jimmfile\"; filename=\"");
            buffer2.append(filename).append("\"\r\n");
            buffer2.append("Content-Type: application/octet-stream\r\n");
            buffer2.append("Content-Transfer-Encoding: binary\r\n");
            buffer2.append("\r\n");
            os.write(StringConvertor.stringToByteArrayUtf8(buffer2.toString()));
            
            // Send file data and show progress
            byte[] buffer = new byte[1024*2];
            int counter = fsize;
            while (counter > 0) {
                int read = fis.read(buffer);
                os.write(buffer, 0, read);
                counter -= read;
                if (fsize != 0) {
                    if (isCanceled()) {
                        throw new JimmException(194, 1);
                    }
                    setProgress((100 - 2) * (fsize - counter) / fsize);
                }
            }
            
            // Send end of header
            String end = "\r\n--" + boundary + "--\r\n";
            os.write(StringConvertor.stringToByteArrayUtf8(end));

            // Read response
            is = sc.openInputStream();
            
            int respCode = sc.getResponseCode();
            if (HttpConnection.HTTP_OK != respCode) {
                throw new JimmException(194, respCode);
            }
            
            StringBuffer response = new StringBuffer();
            for (;;) {
                int read = is.read();
                if (read == -1) break;
                response.append((char)(read & 0xFF));
            }
            
            String respString = response.toString();
            
            int dataPos = respString.indexOf("http://");
            if (-1 == dataPos) {
                // #sijapp cond.if modules_DEBUGLOG is "true" #
                DebugLog.println("server say '" + respString + "'");
                // #sijapp cond.end#
                throw new JimmException(194, 1);
            }
            
            if (isCanceled()) {
                throw new JimmException(194, 1);
            }
            respString = Util.replace(respString, "\r", "");
            respString = Util.replace(respString, "\n", "");
            
            // Send info about file
            StringBuffer messText = new StringBuffer();
            if (!StringConvertor.isEmpty(description)) {
                messText.append(description).append("\n");
            }
            messText.append("File: ").append(filename).append("\n");
            messText.append("Size: ")
                    .append(StringConvertor.bytesToSizeString(fsize, false))
                    .append("\n");
            messText.append("Link: ").append(respString);
            
            protocol.sendMessage(cItem, messText.toString(), true);
            setProgress(100);
            try {
                sc.close();
                os.close();
                is.close();
            } catch (IOException e) {
            }
            
        } catch (IOException e) {
            try {
                sc.close();
            } catch (IOException ex) {
            }
            // #sijapp cond.if modules_DEBUGLOG is "true" #
            DebugLog.panic("send file", e);
            // #sijapp cond.end#
            throw new JimmException(194, 0);
        }
    }
    public void processPhoto(byte[] data) {
        setData(new ByteArrayInputStream(data), data.length);
        String photoname = "photo-"
                + Util.getDateString(false)
                + ".jpeg";
        askForNameDesc(photoname.replace(' ', '-'), "");
    }
}
// #sijapp cond.end#